﻿using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using T.FAS.Runtime.Base.Common;
using T.FAS.Runtime.Base.Dao;
using T.FAS.Runtime.Base.Entity;
using T.ServiceComponent.DataAccessEx.Core;
using T.ServiceComponent.Kernel.Service;
using T.ServiceComponent.Log.SPI;

namespace T.FAS.Runtime.Base.Service
{
    public class FasSplitLogService
    {
        private static readonly object syncObj = new object();

        private readonly ConcurrentBag<DataChangeLog> logStore = null;
        private static FasSplitLogService instance;
        private bool isExist = false;
        private AutoResetEvent autoResetLog;
        private readonly int capacity = 100000;
        private readonly ChangeLogBillSplitDao dao = new ChangeLogBillSplitDao();
        private bool isFull = false;
        private FasSplitLogService()
        {
            logStore = new ConcurrentBag<DataChangeLog>();
            autoResetLog = new AutoResetEvent(false);
            StartLogProcess();
        }

        public void Stop()
        {
            isExist = true;
            autoResetLog.Set();
        }

        public void Restart()
        {
            isExist = false;
            autoResetLog = new AutoResetEvent(false);
            StartLogProcess();
        }


        public static FasSplitLogService GetInstance()
        {
            if (instance == null)
            {
                lock (syncObj)
                {
                    if (instance == null)
                    {
                        instance = new FasSplitLogService();
                    }
                }
            }

            return instance;
        }

        public void AddLog(DataChangeLog log)
        {
            if (log == null) return;

            if (isFull)
            {
                return;
            }

            logStore.Add(log);
        }

        public void AddLogs(IEnumerable<DataChangeLog> logs)
        {
            if (logs == null || logs.Count() == 0)
            {
                return;
            }

            if (isFull)
            {
                return;
            }

            foreach (var i in logs)
            {
                if (i != null)
                {
                    logStore.Add(i);
                }
            }
        }

        private void StartLogProcess()
        {
            Task.Factory.StartNew(() =>
            {
                //重新初始化一下RequestID，防止日志记录在正常链路中。
                //同时这些日志不需要记录全链路了，因为没有意义，所以初始化RequestID为空
                RpcContext.Current.RequestID = "";
                while (!isExist)
                {
                    var start = DateTime.Now;
                    var logs = new List<DataChangeLog>();
                    DataChangeLog log = null;
                    var index = 0;

                    if (logStore?.Count >= capacity)
                    {
                        isFull = true;
                    }
                    else
                    {
                        isFull = false;
                    }

                    if (logStore?.IsEmpty == false)
                    {
                        while ((DateTime.Now - start).TotalMilliseconds < 1000 && index < 10)
                        {
                            var result = logStore.TryTake(out log);
                            if (result && log != null)
                            {
                                index++;
                                logs.Add(log);
                            }
                        }
                    }
                    try
                    {
                        if (logs != null && logs.Count > 0)
                        {
                            foreach (var item in logs)
                            {
                                LogUtil.Log($"AddLogToDB：{item.ID}", "ChangeLog");
                                AddLogToDB(item);
                            }
                            logs.Clear();
                            index = 0;
                        }
                        else
                        {
                            autoResetLog.WaitOne(1000);
                        }
                    }
                    catch (Exception e)
                    {
                        LogUtil.Log(e.ToString(), "ChangeLogError");
                    }
                }
            });
        }
        private void AddLogToDB(DataChangeLog item)
        {
            var billSplit = new ChangeLogBillSplit();
            if (item.Action == "Create")
            {
                billSplit.ID = item.BillID;
                billSplit.TableNameSuf = DateTime.Now.ToString("yyyyMM");
                billSplit.CreateTime = DateTime.Now;
                dao.Add(new List<object> { billSplit });
            }
            else
            {
                billSplit = dao.GetObject(item.BillID);
            }
            if (!string.IsNullOrEmpty(billSplit?.TableNameSuf))
            {
                if (billSplit?.TableNameSuf != "000101")
                {
                    List<string> sqls = new List<string>();
                    string logId = Guid.NewGuid().ToString();
                    var sql = $@"
INSERT INTO Fas_ChangeLog{billSplit.TableNameSuf} (ID, CreateTime, BillID, OldStateCode, OldStateName, NewStateCode, NewStateName, CDOID, TenantID, CDOCode, CDOName, UserID, UserName, Action)
VALUES ('{logId}', '{item.CreateTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}', '{item.BillID}', '{item.Ext11}', '{item.Ext12}','{item.Ext13}', '{item.Ext14}', '{item.Ext15}', '{item.Ext16}', '{item.Ext17}', '{item.Ext18}', '{item.Ext19}', '{item.Ext19}', '{item.Action}');";
                    IDbDataParameter[][] paramList = new IDbDataParameter[item.Datas.Count][];

                    for (int i = 0; i < item.Datas.Count; i++)
                    {
                        List<IDbDataParameter> paramLists = new List<IDbDataParameter>();
                        paramLists.Add(dao.DB.MakeInParam("@OldData", TDbDataType.VarChar, item.Datas[i].OldData));
                        paramLists.Add(dao.DB.MakeInParam("@NewData", TDbDataType.VarChar, item.Datas[i].NewData));
                        paramList[i] = paramLists.ToArray();
                        sqls.Add($@"INSERT INTO Fas_ChangeLogDetail{billSplit.TableNameSuf} (ID, SortOrder, ChangeLogID, BillID, DataColumn, OldData, NewData)
VALUES('{Guid.NewGuid()}', '{i}', '{logId}', '{item.BillID}', '{item.Datas[i].DataColumn}', @OldData, @NewData); ");
                    }
                    using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
                    {
                        dao.DB.ExecSqlStatement(sql);
                        dao.DB.ExecSqlStatement(sqls.ToArray(), paramList.ToArray());
                        scope.Complete();
                    }
                }
                else
                {
                    LogUtil.Log(JsonConvert.SerializeObject(billSplit), "ChangeLog");
                }
            }
            else
            {
                LogUtil.Log(JsonConvert.SerializeObject(item), "ChangeLog");
            }
        }
    }
}
